/*
尽可能还原 Promise 中的每一个 API, 并通过注释的方式描述思路和原理.
*/

// Mypromise 有三种状态
// 状态的变化有两种 PENDING -> FULLFILLED; PENDING -> REJECTED
const PENDING = "PENDING";
const FULLFILLED = "FULLFILLED";
const REJECTED = "REJECTED";
class MyPromise {
  constructor(excutor) {
    try {
      excutor(this.resolve, this.reject);
    } catch (err) {
      this.reject(err);
    }
  }
  status = PENDING;
  // 保存执行成功的结果
  value = undefined;
  // 保存执行失败的原因
  error = undefined;
  // 分别用来保存 then 中传过来的成功回调和失败回调
  resolveCallbacks = [];
  rejectCallbacks = [];

  resolve = (value) => {
    // 只有 PENDGING 状态才能变成 FULLFILLED,改变后状态不可变
    if (this.status === PENDING) {
      this.status = FULLFILLED;
      this.value = value;
      // 当异步 resolve 时，循环从队列中取出 then 缓存的 successCallback 执行
      while (this.resolveCallbacks.length > 0) {
        this.resolveCallbacks.shift()(value);
      }
    }
  };

  reject = (error) => {
    // 只有 PENDGING 状态才能变成 REJECTED,改变后状态不可变
    if (this.status === PENDING) {
      this.status = REJECTED;
      this.error = error;
      // 当异步 reject 时，循环从队列中取出 then 缓存的 errorCallback 执行
      while (this.rejectCallbacks.length) {
        this.rejectCallbacks.shift()(error);
      }
    }
  };

  then(successCallback, errorCallback) {
    successCallback =
      typeof successCallback === "function"
        ? successCallback
        : (value) => value;
    errorCallback =
      typeof errorCallback === "function" ? errorCallback : (value) => value;

    // then 的链式调用，每一次 then 的执行需要返回一个新的 Mypromise
    const p = new MyPromise((resolve, reject) => {
      if (this.status === FULLFILLED) {
        // 这里用 setTimeout 是为了延迟执行，得到 p 这个变量
        setTimeout(() => {
          try {
            // 处理同步 resolve
            // 获取 then 中 successCallback 的结果，resolve 出去，传给下一个 then
            const successRes = successCallback(this.value);
            // 如果结果是 Mypromise 对象返回 Mypromise 对象的结果，可以通过 then 来获取
            // 如果结果是 个普通的值或对象直接返回
            dealWithRes(p, successRes, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          // 这里用 setTimeout 是为了延迟执行，得到 p 这个变量
          try {
            // 处理同步 reject
            // 获取 then 中 errorCallback 的结果，reject 出去，传给下一个 then
            const errorRes = errorCallback(this.error);
            dealWithRes(p, errorRes, resolve, reject);
          } catch (e) {
            reject(e);
          }
        }, 0);
      } else {
        // 处理异步逻辑，在 resolve 或者 reject 未完成是，缓存 successCallback， errorCallback
        this.resolveCallbacks.push(() => {
          // 这里用 setTimeout 是为了延迟执行，得到 p 这个变量
          setTimeout(() => {
            try {
              // 处理同步 resolve
              // 获取 then 中 successCallback 的结果，resolve 出去，传给下一个 then
              const successRes = successCallback(this.value);
              // 如果结果是 Mypromise 对象返回 Mypromise 对象的结果，可以通过 then 来获取
              // 如果结果是 个普通的值或对象直接返回
              dealWithRes(p, successRes, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
        this.rejectCallbacks.push(() => {
          // 这里用 setTimeout 是为了延迟执行，得到 p 这个变量
          setTimeout(() => {
            try {
              // 处理同步 reject
              // 获取 then 中 errorCallback 的结果，reject 出去，传给下一个 then
              const errorRes = errorCallback(this.error);
              dealWithRes(p, errorRes, resolve, reject);
            } catch (e) {
              reject(e);
            }
          }, 0);
        });
      }
    });
    return p;
  }

  catch(handleError) {
    // catch 其实就是包装一下 then 中的 errorCallback
    return this.then(undefined, handleError);
  }

  // 不管执行成功，或者执行失败，都进行调用
  // 本质上是在 fn 插入到 then 的successCallback 和 errorCallback 中执行,再将原本的结果进行透传
  finally(fn) {
    return this.then(
      (value) => {
        fn();
        return value;
      },
      (error) => {
        fn();
        // this.reject(error)
        // return error // 这里不能直接 return error 是因为会直接传给下一个 then 的 successCallback
        return MyPromise.reject(error);
      }
    );
  }

  static resolve(value) {
    if (value instanceof MyPromise) {
      return value;
    } else {
      return new MyPromise((resolve, reject) => {
        resolve(value);
      });
    }
  }
  static reject(error) {
    return new MyPromise((resolve, reject) => {
      reject(error);
    });
  }

  static all(arr) {
    return new MyPromise((resolve, reject) => {
      let result = [];
      let count = 0;
      for (let i = 0; i < arr.length; i++) {
        if (arr[i] instanceof MyPromise) {
          // 异步获取 MyPromise 返回的值
          arr[i].then(
            (res) => {
              result[i] = res;
              count += 1;
              if (count === arr.length) {
                resolve(result);
              }
            },
            (error) => {
              reject(error);
            }
          );
        } else {
          count += 1;
          result[i] = arr[i];
        }
      }
    });
  }
}

function dealWithRes(p, res, resolve, reject) {
  if (p === res) {
    reject(new TypeError("circular reference"));
  } else if (res instanceof MyPromise) {
    // 加上异常捕获，防止返回的 Mypromise 执行报错
    try {
      res.then((result) => resolve(result));
    } catch (e) {
      reject(e);
    }
  } else {
    resolve(res);
  }
}

// const p1 = new MyPromise((resolve, reject) => {
//   setTimeout(() => {
//     resolve("p1");
//   }, 1000);
// });
// const p2 = new MyPromise((resolve, reject) => {
//   setTimeout(() => {
//     resolve("p2");
//   }, 2000);
// });
// const p3 = new MyPromise((resolve, reject) => {
//   setTimeout(() => {
//     reject("p3");
//   }, 1500);
// });

// MyPromise.all([1, 2, p1, 3, p2, p3])
//   .finally(() => {
//     console.log("finally done");
//   })
//   .then(
//     (res) => {
//       console.log("res: ", res);
//     },
//     (error) => {
//       console.log("error===: ", error);
//     }
//   );
